package old.galaxyGeneration;

import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;

import objects.GameSettings;
import objects.Universe;
import objects.physicalEntities.Star;

import inputOutput.PlayerSettings;
import inputOutput.XMLoader;

public class SpiralGalaxy extends Galaxy
{
	//player defined (player settings)
	//--------------------------------------------------
	private int numSpiralArms;  //2 to 10
	//--------------------------------------------------
	
	
	//XML
	//--------------------------------------------------
	//fraction of diskradius that ones and twos extend out from arms in spiral galaxies.
	//larger numbers lead to fuzzier arms
	double fractionOnes;
	double fractionTwos;	
	double diskRadiusFactor;  //associated with radius of central galactic disk
	double spiralRotation;  //basic spiral rotation parameter
	double spiralWhiplashFactor;  //affects how tightly the spiral will coil
	double onesDistFraction;  //fraction of disk beyond which extend ones
	double twosDistFraction;  //fraction of disk beyond which extend twos
	double armLength;  //fraction that determines galaxy arm length.  Smaller equals longer
	//--------------------------------------------------

	public SpiralGalaxy(){}

	public SpiralGalaxy(GameSettings gameSettings, Universe theUniverse) 
	{
		super(gameSettings, theUniverse);		
		//add spiral-specific settings
		numSpiralArms = gameSettings.getNumSpiralArms();
		fractionOnes = gameSettings.getFractionOnes();
		fractionTwos = gameSettings.getFractionTwos();
		diskRadiusFactor = gameSettings.getDiskRadiusFactor();
		spiralRotation = gameSettings.getSpiralRotation();
		spiralWhiplashFactor = gameSettings.getSpiralWhiplashFactor();
		onesDistFraction = gameSettings.getOnesDistFraction();
		twosDistFraction = gameSettings.getTwosDistFraction();
		armLength = gameSettings.getArmLength();
		
		//XMLoader.loadData(this, "Settings\\SpiralGalaxyMap.xml", "Settings\\SpiralGalaxy.xml");		
		//

		this.galaxyGenerator();

	}

	protected void galaxyGenerator()
	{
		System.out.println("Just inside Spiral");
				
		int rotX;  //rotated x values
		int rotY;  //rotated y values
		
		int diskRadius;  //radius of central galactic disk
		
		double distance;  //distance between two points in grid
		
		double theta;  //angle between arms of the galaxy
		
		double starBorderZone1 = 3;
		double starBorderZone2 = uniLength-4;

		if (numStars < 100)
		{System.out.println("Need at least 100 stars!");
		return;}

		//Set array to all 3's
		for (int i = 0; i < uniLength; i++)
		{
			for (int j = 0; j < uniLength; j++)
			{
				starGrid[i][j] = 3;
			}
		}

		diskRadius = (int) (workingLength/diskRadiusFactor);

		//Fill in central disk with 0s
		for (int i = uniLength/4; i < uniLength*3/4; i++)
		{
			for (int j = uniLength/4; j < uniLength*3/4; j++)
			{
				//distance from center of full grid
				distance = Math.sqrt((i-uniLength/2)*(i-uniLength/2) + (j-uniLength/2)*(j-uniLength/2));

				//enable star placement in the disk
				if (distance <= diskRadius)
				{starGrid[i][j] = 0;}
				else if ((distance <= diskRadius*onesDistFraction) & (distance > diskRadius))
				{starGrid[i][j] = 1;}
				else if ((distance <= diskRadius*twosDistFraction) & (distance > diskRadius*onesDistFraction))
				{starGrid[i][j] = 2;}
			}				
		}

		//Create one arm, to the "north" of the disk			
		//Extend initial line of 0 to within armLength*workingLength of border
		for (int i = uniLength/4+(workingLength/2-diskRadius); i > uniLength/4+armLength*workingLength; i--)
		{starGrid[i][uniLength/2] = 0;}

		theta = (Math.PI - 2*Math.PI/numSpiralArms)/2;
		
		//width of one half of the column (effectively, the column's "radius")
		int width = (int) (diskRadius*Math.cos(theta));
		
		//height of column of 0s
		int columnHeight = (int) ((0.5-armLength)*workingLength);
		
		//distance that ones and twos extend from the sides of the arm.
		int onesExtent = (int) (fractionOnes*diskRadius);
		int twosExtent = (int) (fractionTwos*diskRadius);			
		//int tempOnesExtent;
		//int tempTwosExtent;

		//Build right side
		for (int j = uniLength/2+width; j > uniLength/2; j--)
		{
			for (int i = uniLength/2; i > (uniLength/2-columnHeight); i--)
			{
				starGrid[i][j] = 0;

				//Fill in adjacent ones and twos
				if (starGrid[i][j+1]!=0)
				{						
					for (int k = 1; k < onesExtent; k++)
					{starGrid[i][j+k] = 1;}
					for (int k = 0; k < twosExtent; k++)
					{starGrid[i][j+onesExtent+k] = 2;}
				}

			}
		}
		//left side
		for (int j = uniLength/2-width; j <= uniLength/2; j++)
		{
			for (int i = uniLength/2; i > (uniLength/2-columnHeight); i--)
			{
				starGrid[i][j] = 0;				

				//Fill in adjacent ones and twos
				if (starGrid[i][j-1]!=0)
				{	
					for (int k = 1; k < onesExtent; k++)
					{starGrid[i][j-k] = 1;}
					for (int k = 0; k < twosExtent; k++)
					{starGrid[i][j-(onesExtent+k)] = 2;}
				}

				if (j == uniLength/2)
				{
					//Algorithm misses space to the right of the tip of the arm, since uniLength/2
					//column is filled in from left.  Fill in blank space with 1s and 2s
					if (starGrid[i][j+1]!=0)
					{
						for (int k = 1; k < onesExtent; k++)
						{starGrid[i][j+k] = 1;}
						for (int k = 0; k < twosExtent; k++)
						{starGrid[i][j+onesExtent+k] = 2;}								
					}

					/*//At tip of arm (no more 0s), fill in remaining 1s and 2s
					if (i == uniLength/2-columnHeight+1)
					{
						//Fill in 1s and 2s until the tip of the 1s is created
						for (int k = 1; k < onesExtent; k++)
						{
							starGrid[i-k][j] = 1;
							tempOnesExtent = onesExtent-k;
							for (int m = 1; m < tempOnesExtent; m++)
							{
								starGrid[i-k][j+m] = 1;
								starGrid[i-k][j-m] = 1;
							}
							for (int m = 0; m < twosExtent; m++)
							{
								starGrid[i-k][j+tempOnesExtent+m] = 2;
								starGrid[i-k][j-(tempOnesExtent+m)] = 2;
							}
						}

						//Fill in 2s after the tip of 1s, until the 2s form a tip
						for (int k = 1; k < twosExtent; k++)
						{
							starGrid[i-(onesExtent+k-1)][j] = 2;
							tempTwosExtent = twosExtent-k;
							for (int m = 1; m < tempTwosExtent; m++)
							{
								starGrid[i-(onesExtent+k-1)][j+m] = 2;
								starGrid[i-(onesExtent+k-1)][j-m] = 2;
							}								
						}								
					}		*/				
				}					
			}
		}

		try {
			BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("test1.txt"));

			for (int i = 0; i < uniLength; i++)
			{
				for (int j = 0; j < uniLength; j++)
				{
					bufferedWriter.write(String.valueOf(starGrid[i][j]));
					//bufferedWriter.write(" ");

					if (j == (uniLength-1))
					{
						bufferedWriter.newLine();
					}
				}
			}


		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		//current rotation offset
		double currRotation = 0;
		double rotPtI;
		double rotPtJ;			

		//Rotate this arm around the disk to create other arms
		//First rotate the upper left quadrant, preserving the original arm extension from rotation
		//Move left-to-right, top-to-bottom
		for (int i = uniLength/2-workingLength/2; i < uniLength/2; i++)
		{
			for (int j = uniLength/2-workingLength/2; j < uniLength/2-(width+onesExtent+twosExtent); j++)
			{
				//shift current i,j coords to be centered at (0,0)
				rotPtI = i - 0.5 - uniLength/2;
				rotPtJ = j - 0.5 - uniLength/2;
				distance = Math.sqrt((rotPtI)*(rotPtI)+(rotPtJ)*(rotPtJ));					

				if (distance > diskRadius)
					currRotation = -2*Math.PI/numSpiralArms;
				else
					currRotation = 0;

				tempX = Math.floor((rotPtJ)*Math.cos(currRotation)+(rotPtI)*Math.sin(currRotation));
				tempY = Math.floor(-(rotPtJ)*Math.sin(currRotation)+(rotPtI)*Math.cos(currRotation));

				rotX = (int) tempX;
				rotY = (int) tempY;

				starGrid[i][j] = starGrid[rotY+uniLength/2][rotX+uniLength/2];
			}							
		}

		//Next, rotate the full lower left quadrant.
		//Move left-to-right, top-to-bottom
		for (int i = uniLength/2; i < uniLength/2+workingLength/2; i++)
		{
			for (int j = uniLength/2-workingLength/2; j < uniLength/2; j++)
			{
				rotPtI = i - 0.5 - uniLength/2;
				rotPtJ = j - 0.5 - uniLength/2;
				distance = Math.sqrt((rotPtI)*(rotPtI)+(rotPtJ)*(rotPtJ));					

				if (distance > diskRadius)
					currRotation = -2*Math.PI/numSpiralArms;
				else
					currRotation = 0;

				tempX = Math.floor((rotPtJ)*Math.cos(currRotation)+(rotPtI)*Math.sin(currRotation));
				tempY = Math.floor(-(rotPtJ)*Math.sin(currRotation)+(rotPtI)*Math.cos(currRotation));

				rotX = (int) tempX;
				rotY = (int) tempY;

				starGrid[i][j] = starGrid[rotY+uniLength/2][rotX+uniLength/2];
			}							
		}

		//Next, rotate the full lower right quadrant.
		//Move left-to-right, bottom-to-top
		for (int i = uniLength/2+workingLength/2; i > uniLength/2; i--)
		{
			for (int j = uniLength/2; j < uniLength/2+workingLength/2; j++)
			{
				rotPtI = i - 0.5 - uniLength/2;
				rotPtJ = j - 0.5 - uniLength/2;
				distance = Math.sqrt((rotPtI)*(rotPtI)+(rotPtJ)*(rotPtJ));					

				if (distance > diskRadius)
					currRotation = -2*Math.PI/numSpiralArms;
				else
					currRotation = 0;

				tempX = Math.floor((rotPtJ)*Math.cos(currRotation)+(rotPtI)*Math.sin(currRotation));
				tempY = Math.floor(-(rotPtJ)*Math.sin(currRotation)+(rotPtI)*Math.cos(currRotation));

				rotX = (int) tempX;
				rotY = (int) tempY;

				starGrid[i][j] = starGrid[rotY+uniLength/2][rotX+uniLength/2];
			}							
		}

		//Finally, rotate the full upper right quadrant.
		//Move left-to-right, bottom-to-top
		for (int i = uniLength/2; i > uniLength/2-workingLength/2; i--)
		{
			for (int j = uniLength/2; j < uniLength/2+workingLength/2; j++)
			{
				rotPtI = i - 0.5 - uniLength/2;
				rotPtJ = j - 0.5 - uniLength/2;
				distance = Math.sqrt((rotPtI)*(rotPtI)+(rotPtJ)*(rotPtJ));					

				if (distance > diskRadius)
					currRotation = -2*Math.PI/numSpiralArms;
				else
					currRotation = 0;

				tempX = Math.floor((rotPtJ)*Math.cos(currRotation)+(rotPtI)*Math.sin(currRotation));
				tempY = Math.floor(-(rotPtJ)*Math.sin(currRotation)+(rotPtI)*Math.cos(currRotation));

				rotX = (int) tempX;
				rotY = (int) tempY;

				starGrid[i][j] = starGrid[rotY+uniLength/2][rotX+uniLength/2];
			}							
		}


		//Create temporary array to act as original reference point during spiral rotation
		int [][] tempStarGrid = new int[uniLength][uniLength];

		//Copy current starGrid to temporary grid
		for (int i = 0; i < uniLength; i++)
		{
			for (int j = 0; j < uniLength; j++)
			{
				tempStarGrid[i][j] = starGrid[i][j];					
			}
		}

		//Create spiral effect			
		for (int i = uniLength/2-workingLength/2; i < uniLength/2+workingLength/2; i++)
		{
			for (int j = uniLength/2-workingLength/2; j < uniLength/2+workingLength/2; j++)
			{
				rotPtI = i - 0.5 - uniLength/2;
				rotPtJ = j - 0.5 - uniLength/2;
				distance = Math.sqrt((rotPtI)*(rotPtI)+(rotPtJ)*(rotPtJ));

				if (distance > diskRadius)
					currRotation = spiralRotation*(distance-diskRadius)/(workingLength/spiralWhiplashFactor);
				else
					currRotation = 0;

				tempX = Math.floor((rotPtJ)*Math.cos(currRotation)+(rotPtI)*Math.sin(currRotation));
				tempY = Math.floor(-(rotPtJ)*Math.sin(currRotation)+(rotPtI)*Math.cos(currRotation));

				rotX = (int) tempX;
				rotY = (int) tempY;

				starGrid[i][j] = tempStarGrid[rotY+uniLength/2][rotX+uniLength/2];

			}
		}


		try {
			BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("test2.txt"));

			for (int i = 0; i < uniLength; i++)
			{
				for (int j = 0; j < uniLength; j++)
				{
					bufferedWriter.write(String.valueOf(starGrid[i][j]));
					//bufferedWriter.write(" ");

					if (j == (uniLength-1))
					{
						bufferedWriter.newLine();
					}
				}
			}


		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}


		//Place stars			
		for (starsPlaced = 0; starsPlaced < numStars; starsPlaced++)
		{
			tempX = uniLength/4+randG1.nextGaussian()*workingLength/2+workingLength/2;
			tempY = uniLength/4+randG2.nextGaussian()*workingLength/2+workingLength/2;

			x = (int)tempX;
			y = (int)tempY;

			//if within the grid and not near edges
			if ((x > starBorderZone1) & (y > starBorderZone1) & (x < starBorderZone2) & (y < starBorderZone2))
			{
				//check starGrid values at location
				if (starGrid[x][y] == 0)
				{
					universe.AddStar(new StarModel(x,y));
					updateGrid(x,y);					
				}

				else if ((starGrid[x][y] == 1) & (randUni.nextDouble() < ringTwo))
				{
					universe.AddStar(new StarModel(x,y));
					updateGrid(x,y);						
				}

				else if ((starGrid[x][y] == 2) & (randUni.nextDouble() < ringOne))
				{
					universe.AddStar(new StarModel(x,y));
					updateGrid(x,y);
				}
				else
				{starsPlaced -= 1;}
				//if outside the grid, subt 1 from i, loop again
			}
			else 
			{starsPlaced -= 1;}						
		}
		universe.PrintStars();
	}

	public double getFractionOnes() {
		return fractionOnes;
	}

	public void setFractionOnes(double fractionOnes) {
		this.fractionOnes = fractionOnes;
	}

	public double getFractionTwos() {
		return fractionTwos;
	}

	public void setFractionTwos(double fractionTwos) {
		this.fractionTwos = fractionTwos;
	}

	public double getDiskRadiusFactor() {
		return diskRadiusFactor;
	}

	public void setDiskRadiusFactor(double diskRadiusFactor) {
		this.diskRadiusFactor = diskRadiusFactor;
	}

	public double getSpiralRotation() {
		return spiralRotation;
	}

	public void setSpiralRotation(double spiralRotation) {
		this.spiralRotation = spiralRotation;
	}

	public double getSpiralWhiplashFactor() {
		return spiralWhiplashFactor;
	}

	public void setSpiralWhiplashFactor(double spiralWhiplashFactor) {
		this.spiralWhiplashFactor = spiralWhiplashFactor;
	}

	public double getOnesDistFraction() {
		return onesDistFraction;
	}

	public void setOnesDistFraction(double onesDistFraction) {
		this.onesDistFraction = onesDistFraction;
	}

	public double getTwosDistFraction() {
		return twosDistFraction;
	}

	public void setTwosDistFraction(double twosDistFraction) {
		this.twosDistFraction = twosDistFraction;
	}

	public double getArmLength() {
		return armLength;
	}

	public void setArmLength(double armLength) {
		this.armLength = armLength;
	}
}
